﻿
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace CatEars.Core.WinAPI
{
#pragma warning disable 1591
    public static class InternetProxy
    {
        public static string DefaultIgnorePath = "<-loopback>;127.0.0.1;192.168;";

        public enum ProxyOpearte
        {
            Attach,
            Detach
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct INTERNET_PER_CONN_OPTION_LIST
        {
            public int Size;
            public string Connection;
            public int OptionCount;
            public int OptionError;
            public IntPtr pOptions;
        }

        [StructLayout(LayoutKind.Explicit)]
        private struct OptionUnion
        {
            [FieldOffset(0)]
            public int dwValue;
            [FieldOffset(0)]
            public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
            [FieldOffset(0)]
            public IntPtr pszValue;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class INTERNET_PER_CONN_OPTION
        {
            public int dwOption;
            public OptionUnion Value;
        }

        [DllImport("wininet.dll", EntryPoint = "InternetSetOption", CharSet = CharSet.Ansi, SetLastError = true)]
        private static extern bool InternetSetOptionList(IntPtr hInternet, int Option, ref INTERNET_PER_CONN_OPTION_LIST OptionList, int size);
        [DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
        private static extern bool InternetSetOption(IntPtr hInternet, int Option, [In] IntPtr buffer, int BufferLength);

        private const int INTERNET_OPTION_PER_CONNECTION_OPTION = 0x4B;
        private const int INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 0x5F;

        private const int INTERNET_PER_CONN_FLAGS = 1;
        private const int INTERNET_PER_CONN_PROXY_SERVER = 2;
        private const int INTERNET_PER_CONN_PROXY_BYPASS = 3;
        private const int INTERNET_PER_CONN_AUTOCONFIG_URL = 4;
        private const int INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5;

        private const int PROXY_TYPE_DIRECT = 0x00000001;    // direct to net    
        private const int PROXY_TYPE_PROXY = 0x00000002;     // via named proxy    

        /// <summary>
        /// 设置系统代理
        /// </summary>
        /// <param name="proxyOperate"></param>
        /// <param name="strHost"></param>
        /// <param name="intPort"></param>
        /// <param name="strIgnore"></param>
        /// <param name="blnIgroreErr"></param>
        /// <returns></returns>
        public static void SetToWinINETAll(
            ProxyOpearte proxyOperate,
            string strHost,
            int intPort,
            string strIgnore,
            bool blnIgroreErr)
        {
            if (blnIgroreErr)
            {
                try
                {
                    SetToWinINET(null, proxyOperate, strHost, intPort, strIgnore);
                }
                catch { }
                var coll = RasInfo.GetAllRasEntryName();
                if (!coll.IsNullOrEmptyT())
                {
                    foreach (var item in coll)
                    {
                        try
                        {
                            SetToWinINET(item, proxyOperate, strHost, intPort, strIgnore);
                        }
                        catch { }
                    }
                }
            }
            else
            {
                SetToWinINET(null, proxyOperate, strHost, intPort, strIgnore);
                var coll = RasInfo.GetAllRasEntryName();
                if (!coll.IsNullOrEmptyT())
                {
                    foreach (var item in coll)
                    {
                        SetToWinINET(item, proxyOperate, strHost, intPort, strIgnore);
                    }
                }
            }
        }

        /// <summary>
        /// 设置系统代理
        /// </summary>
        /// <param name="connectionName">null表示设置局域网代理</param>
        /// <param name="proxyOperate"></param>
        /// <param name="strHost"></param>
        /// <param name="intPort"></param>
        /// <param name="strIgnore"></param>
        /// <returns></returns>
        public static bool SetToWinINET(
            string connectionName,
            ProxyOpearte proxyOperate,
            string strHost,
            int intPort,
            string strIgnore)
        {
            try
            {
                INTERNET_PER_CONN_OPTION_LIST structure = new INTERNET_PER_CONN_OPTION_LIST();
                INTERNET_PER_CONN_OPTION[] internet_per_conn_options = new INTERNET_PER_CONN_OPTION[5];
                structure.Connection = connectionName;
                structure.OptionCount = internet_per_conn_options.Length;
                structure.OptionError = 0;
                int num = 0;

                num |= PROXY_TYPE_DIRECT;
                if (proxyOperate == ProxyOpearte.Attach)
                {
                    num |= PROXY_TYPE_PROXY;
                }

                internet_per_conn_options[0] = new INTERNET_PER_CONN_OPTION();
                internet_per_conn_options[0].dwOption = INTERNET_PER_CONN_FLAGS;  // 1 
                internet_per_conn_options[0].Value.dwValue = num;
                internet_per_conn_options[1] = new INTERNET_PER_CONN_OPTION();
                internet_per_conn_options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;  // 2   
                string strHostValue = "";
                if (intPort > 0)
                {
                    strHostValue = "http=" + strHost + ":" + intPort + ";";
                }
                internet_per_conn_options[1].Value.pszValue =
                    Marshal.StringToHGlobalAnsi(strHostValue);
                internet_per_conn_options[2] = new INTERNET_PER_CONN_OPTION();
                internet_per_conn_options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; // 3  
                internet_per_conn_options[2].Value.pszValue = Marshal.StringToHGlobalAnsi(strIgnore);
                internet_per_conn_options[3] = new INTERNET_PER_CONN_OPTION();
                internet_per_conn_options[3].dwOption = INTERNET_PER_CONN_AUTOCONFIG_URL;  // 4  
                internet_per_conn_options[3].Value.pszValue = Marshal.StringToHGlobalAnsi(null);
                internet_per_conn_options[4] = new INTERNET_PER_CONN_OPTION();
                internet_per_conn_options[4].dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;  // 5  
                internet_per_conn_options[4].Value.dwValue = 0;

                int cb = 0;
                for (int i = 0; i < internet_per_conn_options.Length; i++)
                {
                    cb += Marshal.SizeOf(internet_per_conn_options[i]);
                }
                IntPtr ptr = Marshal.AllocCoTaskMem(cb);
                IntPtr ptr2 = ptr;
                for (int j = 0; j < internet_per_conn_options.Length; j++)
                {
                    Marshal.StructureToPtr(internet_per_conn_options[j], ptr2, false);
                    ptr2 = (IntPtr)(((long)ptr2) + Marshal.SizeOf(internet_per_conn_options[j]));
                }
                structure.pOptions = ptr;
                structure.Size = Marshal.SizeOf(structure);
                int size = structure.Size;
                bool flag = InternetSetOptionList(IntPtr.Zero, INTERNET_OPTION_PER_CONNECTION_OPTION, ref structure, size);
                int num6 = Marshal.GetLastWin32Error();
                if (flag)
                {
                    InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PROXY_SETTINGS_CHANGED, IntPtr.Zero, 0);
                }
                else
                {
                    Trace.WriteLine("SetProxy failed.  WinINET Error #" + num6.ToString("x"));
                }
                Marshal.FreeHGlobal(internet_per_conn_options[0].Value.pszValue);
                Marshal.FreeHGlobal(internet_per_conn_options[1].Value.pszValue);
                Marshal.FreeHGlobal(internet_per_conn_options[2].Value.pszValue);
                Marshal.FreeCoTaskMem(ptr);
                return flag;
            }
            catch (Exception exception)
            {
                Trace.WriteLine("SetProxy failed. " + exception.Message);
                return false;
            }
        }
    }
}
